﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace SoundEngine3.AudioFile
{
    public sealed class NOggFileDescription : AudioFileDescription
    {
        public override string Name
        {
            get { return "Ogg Vorbis"; }
        }

        public override string FilenameType
        {
            get { return "ogg"; }
        }

        public override bool SupportDecode
        {
            get
            {
                return true;
            }
        }

        public override bool SupportEncode
        {
            get
            {
                return false;
            }
        }

        public override bool FileSupported(Stream stream)
        {
            // TODO: Make a litle more better check than just 4 UTF8 chars...
            byte[] magicNumber = new byte[4];
            stream.Read(magicNumber, 0, magicNumber.Length);
            string text = UTF8Encoding.UTF8.GetString(magicNumber, 0, magicNumber.Length);
            if (text.Equals("OggS"))
            {
                return true;
            }

            return false;
        }

        public override AudioFilePathBase CreateInstance()
        {
            return new NOggFile(this);
        }
    }

    public struct NOggDescription : IAudioDescription
    {

    }

    public class NOggFile : AudioFileBase<NOggDescription>
    {
        private short[] AudioLeft;
        private short[] AudioRight;
        private int AudioLength;
        private int m_bufferCount;
        private int m_length;
        private int m_bufferIndex = -1;

        private Dictionary<string, string> mTags = new Dictionary<string, string>();

        public NOggFile(AudioFileDescription description) : base(description)
        {
        }

        public override void Create(Stream stream, NOggDescription audioDescription)
        {
            throw new NotImplementedException();
        }

        public override void OnLoad(Stream stream)
        {
            using (var vorbis = new NVorbis.VorbisReader(stream, true))
            {
                int channels = vorbis.Channels;
                SourceSampleRate = vorbis.SampleRate;
                TimeSpan duration = vorbis.TotalTime;

                InitializeTags(vorbis);
                
                // Start loop.
                if (mTags.ContainsKey("START"))
                {
                    LoopStartSample = int.Parse(mTags["START"]);
                }

                // End loop.
                if (mTags.ContainsKey("END"))
                {
                    LoopEndSample = int.Parse(mTags["END"]);
                }

                AudioLeft = new short[vorbis.TotalSamples];
                AudioRight = new short[vorbis.TotalSamples];
                AudioLength = (int)vorbis.TotalSamples;

                float[] buffer = new float[16384];
                int count;
                int j = 0;
                while ((count = vorbis.ReadSamples(buffer, 0, buffer.Length)) > 0)
                {
                    // Do stuff with the samples returned...
                    // Sample value range is -0.99999994f to 0.99999994f
                    // Samples are interleaved (chan0, chan1, chan0, chan1, etc.)

                    if (channels == 1)
                    {
                        for (int i = 0; i < count; i += 1)
                        {
                            if (j >= AudioLength)
                            {
                                break;
                            }
                            AudioLeft[j] = (short)(buffer[i] * short.MaxValue);
                            AudioRight[j] = (short)(buffer[i] * short.MaxValue);
                            ++j;
                        }
                    }
                    else if (channels == 2)
                    {
                        for (int i = 0; i < count; i += 2)
                        {
                            if (j >= AudioLength)
                            {
                                break;
                            }
                            AudioLeft[j] = (short)(buffer[i] * short.MaxValue);
                            AudioRight[j] = (short)(buffer[i + 1] * short.MaxValue);
                            ++j;
                        }
                    }
                }
            }
        }

        private void InitializeTags(NVorbis.VorbisReader vorbis)
        {
            string[] comments = vorbis.Comments;

            foreach (var comment in comments)
            {
                if (comment.Contains("="))
                {
                    string[] splitted = comment.Split('=');
                    string key = splitted[0];
                    string value = splitted[1];

                    if (!mTags.ContainsKey(key))
                    {
                        mTags.Add(key, value);
                    }
                }
            }
        }

        public override void Create(Stream stream, IAudioDescription audioDescription)
        {
            throw new NotImplementedException();
        }

        public override IAudioDescription CreateAudioDescriptionFromSetup(SoundEngine soundEngine)
        {
            return new NOggDescription();
        }

        public override bool IsValid
        {
            get { return true; }
        }

        public override double Length
        {
            get { return AudioLeft.Length; }
        }
        
        public override bool CanStream
        {
            get { return false; }
        }

        public override void Dispose()
        {

        }

        public override bool IsRecording
        {
            get
            {
                return false;
            }
        }

        public override double GetSample(int channelIndex, int index)
        {
            if (channelIndex == 0)
            {
                return AudioLeft[index];
            }
            else
            {
                return AudioRight[index];
            }
        }
        
        public override int Record(AudioProcessing.Channel left, AudioProcessing.Channel right, long audioFileSamplePosition, int channelsSampleOffset, int sampleLength)
        {
            throw new NotImplementedException();
        }
    }
}
